import 'dart:async' show StreamSink;
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_k_chart/k_chart_widget.dart';

import '../entity/info_window_entity.dart';
import '../entity/k_line_entity.dart';
import '../utils/date_format_util.dart';
import 'base_chart_painter.dart';
import 'base_chart_renderer.dart';
import 'main_renderer.dart';
import 'secondary_renderer.dart';
import 'vol_renderer.dart';

class ChartPainter extends BaseChartPainter {
  static get maxScrollX => BaseChartPainter.maxScrollX;
  BaseChartRenderer? mMainRenderer, mVolRenderer, mSecondaryRenderer;
  StreamSink<InfoWindowEntity?>? sink;
  AnimationController? controller;
  double opacity;
  ValueNotifier<Rect?>? onRealtimeRectNotifier;
  final bool isCustomChart;
  ChartPainter({
    required List<KLineEntity> datas,
    required double scaleX,
    required double scrollX,
    required bool isLongPass,
    required double selectX,
    double? selectY,
    required ChartStyle chartStyle,
    required ChartColors chartColors,
    this.onRealtimeRectNotifier,
    required MainState mainState,
    required VolState volState,
    required SecondaryState secondaryState,
    this.sink,
    bool isLine = false,
    required this.isCustomChart,
    this.controller,
    this.opacity = 0.0,
  }) : super(
          datas: datas,
          scaleX: scaleX,
          scrollX: scrollX,
          isShowCrossLine: isLongPass,
          selectX: selectX,
          selectY: selectY,
          mainState: mainState,
          volState: volState,
          secondaryState: secondaryState,
          isLine: isLine,
          chartStyle: chartStyle,
          chartColors: chartColors,
        );

  @override
  void initChartRenderer() {
    mMainRenderer ??= MainRenderer(
      mainRect: mMainRect ?? Rect.zero,
      maxValue: mMainMaxValue,
      minValue: mMainMinValue,
      topPadding: chartStyle.topPadding ?? 0.0,
      state: mainState,
      isLine: isLine,
      scaleX: scaleX,
      chartColors: chartColors,
      chartStyle: chartStyle,
      isCustomKChart: isCustomChart,
    );
    if (mVolRect != null) {
      mVolRenderer ??= VolRenderer(
        mainRect: mVolRect!,
        maxValue: mVolMaxValue,
        minValue: mVolMinValue,
        topPadding: chartStyle.childPadding ?? 0.0,
        scaleX: scaleX,
        chartColors: chartColors,
        chartStyle: chartStyle,
      );
    }
    if (mSecondaryRect != null) {
      mSecondaryRenderer ??= SecondaryRenderer(
        mainRect: mSecondaryRect!,
        maxValue: mSecondaryMaxValue,
        minValue: mSecondaryMinValue,
        topPadding: chartStyle.childPadding ?? 0.0,
        state: secondaryState!,
        scaleX: scaleX,
        chartColors: chartColors,
        chartStyle: chartStyle,
      );
    }
  }

  Paint get mBgPaint {
    var paint = Paint();
    paint..color = chartColors.bgColor ?? paint.color;
    return paint;
  }

  @override
  void drawBg(Canvas canvas, Size size) {
    if (mMainRect != null) {
      Rect mainRect = Rect.fromLTRB(
          0, 0, mMainRect!.width, mMainRect!.height + chartStyle.topPadding!);
      canvas.drawRect(mainRect, mBgPaint);
    }

    if (mVolRect != null) {
      Rect volRect = Rect.fromLTRB(0, mVolRect!.top - chartStyle.childPadding!,
          mVolRect!.width, mVolRect!.bottom);
      canvas.drawRect(volRect, mBgPaint);
    }

    if (mSecondaryRect != null) {
      Rect secondaryRect = Rect.fromLTRB(
          0,
          mSecondaryRect!.top - chartStyle.childPadding!,
          mSecondaryRect!.width,
          mSecondaryRect!.bottom);
      canvas.drawRect(secondaryRect, mBgPaint);
    }
    Rect dateRect = Rect.fromLTRB(
        0, size.height - chartStyle.bottomDateHigh!, size.width, size.height);
    canvas.drawRect(dateRect, mBgPaint);
  }

  @override
  void drawGrid(canvas) {
    mMainRenderer?.drawGrid(
        canvas, chartStyle.gridRows!, chartStyle.gridColumns!);
    mVolRenderer?.drawGrid(
        canvas, chartStyle.gridRows!, chartStyle.gridColumns!);
    mSecondaryRenderer?.drawGrid(
        canvas, chartStyle.gridRows!, chartStyle.gridColumns!);
  }

  @override
  void drawChart(Canvas canvas, Size size) {
    canvas.save();
    canvas.translate(mTranslateX * scaleX, 0.0);
    //画布水平缩放
    canvas.scale(scaleX, 1.0);
    for (int i = mStartIndex; datas != null && i <= mStopIndex; i++) {
      KLineEntity curPoint = datas![i];
      // if (curPoint == null) continue;
      KLineEntity lastPoint = (i == 0 ? curPoint : datas![i - 1]);
      double curX = getX(i);
      double lastX = i == 0 ? curX : getX(i - 1);

      mMainRenderer?.drawChart(lastPoint, curPoint, lastX, curX, size, canvas);
      mVolRenderer?.drawChart(lastPoint, curPoint, lastX, curX, size, canvas);
      mSecondaryRenderer?.drawChart(
          lastPoint, curPoint, lastX, curX, size, canvas);
    }

    if (isShowCrossLine == true) drawCrossLine(canvas, size);
    canvas.restore();
  }

  @override
  void drawRightText(canvas) {
    var textStyle =
        getTextStyle(chartColors.yAxisTextColor!, fontWeight: FontWeight.w500);
    mMainRenderer?.drawRightText(canvas, textStyle, chartStyle.gridRows!);
    mVolRenderer?.drawRightText(canvas, textStyle, chartStyle.gridRows!);
    mSecondaryRenderer?.drawRightText(canvas, textStyle, chartStyle.gridRows!);
  }

  @override
  void drawDate(Canvas canvas, Size size) {
    double columnSpace = size.width / chartStyle.gridColumns!;
    double startX = getX(mStartIndex) - mPointWidth / 2;
    double stopX = getX(mStopIndex) + mPointWidth / 2;
    double y = 0.0;
    for (var i = 0; i <= chartStyle.gridColumns!; ++i) {
      double translateX = xToTranslateX(columnSpace * i);
      if (translateX >= startX && translateX <= stopX) {
        int index = indexOfTranslateX(translateX);
        if (datas![index] == null) continue;
        TextPainter tp = getTextPainter(
          getDate(datas![index].id),
          color: chartColors.xAxisTextColor,
          fontWeight: FontWeight.w500,
        );
        y = size.height -
            chartStyle.bottomDateHigh! +
            (chartStyle.bottomDatePaddingTop ?? 0.0);
        tp.paint(canvas, Offset(columnSpace * i - tp.width / 2, y));
      }
    }
  }

  Paint get selectPointPaint {
    final paint = Paint();
    paint
      ..isAntiAlias = true
      ..strokeWidth = 0.5
      ..color = chartColors.selectYAxisTextBgColor!;
    return paint;
  }

  // Paint get selectorBorderPaint => Paint()
  //   ..isAntiAlias = true
  //   ..strokeWidth = 0.5
  //   ..style = PaintingStyle.stroke
  //   ..color = chartColors.selectYAxisTextBgColor;

  @override
  void drawCrossLineText(Canvas canvas, Size size) {
    if (null == selectX) return;
    var index = calculateSelectedX(selectX!);
    Object? obj = getItem(index);
    if (obj is! KLineEntity) return;
    KLineEntity point = obj;
    double y = getMainY(point.close);
    var price = point.close;

    //点击的位置
    if (selectY != null) {
      // debugPrint(
      //     'ChartPainter.drawCrossLineText selectY=$selectY  mMainMinValue=$mMainMinValue');
      // selectY = size.height;

      var value = mMainRenderer?.getPrice(selectY!);
      if (value != null) {
        y = selectY!;
        price = value;
      }
    }
    TextPainter tp =
        getTextPainter(format(price), color: chartColors.selectYAxisTextColor);
    double textHeight = tp.height;
    double textWidth = tp.width;

    double w1 = 5;
    double w2 = 3;
    double r = textHeight / 2 + w2;

    double x;
    bool isLeft = false;
    if (translateXtoX(getX(index)) < mWidth / 2) {
      isLeft = false;
      x = 1;
      // Path path = new Path();
      // path.moveTo(x, y - r);
      // path.lineTo(x, y + r);
      // path.lineTo(textWidth + 2 * w1, y + r);
      // path.lineTo(textWidth + 2 * w1 + w2, y);
      // path.lineTo(textWidth + 2 * w1, y - r);
      // path.close();
      // canvas.drawPath(path, selectPointPaint);
      // canvas.drawPath(path, selectorBorderPaint);
      // tp.paint(canvas, Offset(x + w1, y - textHeight / 2));
    } else {
      isLeft = true;
      // x = mWidth - textWidth - 1 - 2 * w1 - w2;
      // Path path = new Path();
      // path.moveTo(x, y);
      // path.lineTo(x + w2, y + r);
      // path.lineTo(mWidth - 2, y + r);
      // path.lineTo(mWidth - 2, y - r);
      // path.lineTo(x + w2, y - r);
      // path.close();
      // canvas.drawPath(path, selectPointPaint);
      // canvas.drawPath(path, selectorBorderPaint);
      // tp.paint(canvas, Offset(x + w1 + w2, y - textHeight / 2));
    }

    var radius = Radius.circular(2);
    // 选中的价格显示在右边
    {
      x = mWidth - textWidth - 2 * w1 - w2;
      var yRrect = RRect.fromLTRBR(x + w2, y - r, mWidth - 2, y + r, radius);
      canvas.drawRRect(yRrect, selectPointPaint);
      // Path path = Path();
      // // path.moveTo(x, y);
      // path.moveTo(x + w2, y + r);
      // path.lineTo(mWidth - 2, y + r);
      // path.lineTo(mWidth - 2, y - r);
      // path.lineTo(x + w2, y - r);
      // canvas.drawPath(path, selectPointPaint);
      // canvas.drawPath(path, selectorBorderPaint);
      tp.paint(canvas, Offset(x + w1 + w2, y - textHeight / 2));
    }

    TextPainter dateTp = getTextPainter(
      getDate(point.id),
      color: chartColors.selectXAxisTextColor,
      fontSize: chartStyle.defaultTextSize,
      height: max(1.0, 12 / chartStyle.defaultTextSize!),
    );
    textWidth = dateTp.width;
    r = textHeight / 2;
    x = translateXtoX(getX(index));
    y = size.height -
        chartStyle.bottomDateHigh! +
        chartStyle.bottomDatePaddingTop!;

    if (x < textWidth + 2 * w1) {
      x = 1 + textWidth / 2 + w1;
    } else if (mWidth - x < textWidth + 2 * w1) {
      x = mWidth - 1 - textWidth / 2 - w1;
    }
    const paddingV = 2;
    double baseLine = textHeight / 2;
    var rect = Rect.fromLTRB(x - textWidth / 2 - w1, y - paddingV,
        x + textWidth / 2 + w1, y + baseLine + r + paddingV);

    var xRrect = RRect.fromRectAndRadius(rect, radius);
    canvas.drawRRect(xRrect, selectPointPaint);
    // 选中的x轴时间
    dateTp.paint(canvas, Offset(x - textWidth / 2, y));

    //长按显示这条数据详情
    sink?.add(InfoWindowEntity(point, isLeft, mFormats));
  }

  @override
  void drawText(Canvas canvas, KLineEntity data, double x) {
    if (null == selectX) return;
    //长按显示按中的数据
    if (isShowCrossLine) {
      var index = calculateSelectedX(selectX!);
      var obj = getItem(index);
      data = obj!;
    }
    //松开显示最后一条数据
    mMainRenderer?.drawText(canvas, data, x);
    mVolRenderer?.drawText(canvas, data, x);
    mSecondaryRenderer?.drawText(canvas, data, x);
  }

  @override
  void drawMaxAndMin(Canvas canvas) {
    if (isLine == true) return;
    //绘制最大值和最小值
    double x = translateXtoX(getX(mMainMinIndex));
    double y = getMainY(mMainLowMinValue);
    if (x < mWidth / 2) {
      //画右边
      TextPainter tp = getTextPainter("── ${format(mMainLowMinValue)}",
          color: chartColors.maxMinTextColor, fontWeight: FontWeight.w500);
      tp.paint(canvas, Offset(x, y - tp.height / 2));
    } else {
      TextPainter tp = getTextPainter("${format(mMainLowMinValue)} ──",
          color: chartColors.maxMinTextColor, fontWeight: FontWeight.w500);
      tp.paint(canvas, Offset(x - tp.width, y - tp.height / 2));
    }
    x = translateXtoX(getX(mMainMaxIndex));
    y = getMainY(mMainHighMaxValue);
    if (x < mWidth / 2) {
      //画右边
      TextPainter tp = getTextPainter("── ${format(mMainHighMaxValue)}",
          color: chartColors.maxMinTextColor, fontWeight: FontWeight.w500);
      tp.paint(canvas, Offset(x, y - tp.height / 2));
    } else {
      TextPainter tp = getTextPainter("${format(mMainHighMaxValue)} ──",
          color: chartColors.maxMinTextColor, fontWeight: FontWeight.w500);
      tp.paint(canvas, Offset(x - tp.width, y - tp.height / 2));
    }
  }

  ///画交叉线
  void drawCrossLine(Canvas canvas, Size size) {
    if (null == selectX) return;
    var index = calculateSelectedX(selectX!);
    KLineEntity point = getItem(index)!;
    Paint paintY = Paint()
      ..color = chartColors.vCrossColor ?? Colors.white
      ..strokeWidth = chartStyle.vCrossWidth! / scaleX
      ..isAntiAlias = true;

    double x = getX(index);
    double y = getMainY(point.close);
    //点击的位置
    if (selectY != null) {
      // debugPrint('ChartPainter.drawCrossLine selectY=$selectY  size=$size');
      y = selectY!;
    }

    // k线图竖线
    canvas.drawLine(
        Offset(
          x,
          0,
          // chartStyle.topPadding,
        ),
        Offset(x, size.height - chartStyle.bottomDateHigh!),
        paintY);

    Paint paintX = Paint()
      ..color = chartColors.hCrossColor ?? Colors.white
      ..strokeWidth = chartStyle.hCrossWidth!
      ..isAntiAlias = true;
    // k线图横线
    canvas.drawLine(Offset(-mTranslateX, y),
        Offset(-mTranslateX + mWidth / scaleX, y), paintX);

    //中心圆形 外圆
    canvas.drawOval(
      Rect.fromCenter(
          center: Offset(x, y), height: 1.5 /** scaleX*/, width: 1.5 / scaleX),
      paintX,
    );
    //内圆
    canvas.drawOval(
      Rect.fromCenter(
          center: Offset(x, y), height: 0.5 /* * scaleX*/, width: 0.5 / scaleX),
      paintX..color = const Color(0xFFFFFFFF),
    );
  }

  final Paint realTimePaint = Paint()
        ..strokeWidth = 0.5
        ..isAntiAlias = true,
      pointPaint = Paint();

  ///画实时价格线
  @override
  void drawRealTimePrice(Canvas canvas, Size size) {
    if (mMarginRight == 0 || datas?.isEmpty == true) return;
    KLineEntity point = datas!.last;
    // var change = point.change ?? (point.close - point.open);
    // var isUp = change>=0;
    //K线的涨跌颜色用同一根的收盘和开盘比较
    var isUp = (point.close - point.open) >= 0;
    var realTimeLineColor = chartColors.realTimeLineColorFn?.call(isUp) ??
        (isUp ? chartColors.upColor : chartColors.dnColor);
    var realTimeLongLineColor =
        chartColors.realTimeLongLineColorFn?.call(isUp) ??
            (isUp ? chartColors.upColor : chartColors.dnColor);

    var realTimeTextColor = chartColors.realTimeTextColorFn?.call(isUp) ??
        (isUp ? chartColors.upColor : chartColors.dnColor);
    var rightRealTimeTextColor =
        chartColors.rightRealTimeTextColorFn?.call(isUp) ??
            (isUp ? chartColors.upColor : chartColors.dnColor);
    TextPainter tp = getTextPainter(format(point.close).trim(),
        color: rightRealTimeTextColor);
    double y = getMainY(point.close);
    //max越往右边滑值越小
    var max = (mTranslateX.abs() +
            mMarginRight -
            getMinTranslateX().abs() +
            mPointWidth) *
        scaleX;
    double x = mWidth - max;
    if (!isLine) x += mPointWidth / 2;
    var dashWidth = 2;
    var dashSpace = 2;
    double startX = 0;
    final space = (dashSpace + dashWidth);
    if (tp.width < max) {
      while (startX < max) {
        canvas.drawLine(
            Offset(x + startX, y),
            Offset(x + startX + dashWidth, y),
            realTimePaint..color = realTimeLineColor ?? realTimePaint.color);
        startX += space;
      }
      //画一闪一闪
      if (isLine) {
        startAnimation();
        Gradient pointGradient = RadialGradient(
            colors: [Colors.white.withOpacity(opacity), Colors.transparent]);
        pointPaint.shader = pointGradient
            .createShader(Rect.fromCircle(center: Offset(x, y), radius: 14.0));
        canvas.drawCircle(Offset(x, y), 14.0, pointPaint);
        canvas.drawCircle(
            Offset(x, y), 2.0, realTimePaint..color = Colors.white);
      } else {
        stopAnimation(); //停止一闪闪
      }
      double left = mWidth - tp.width - chartStyle.rightTextMarginRight!;
      double top = y - tp.height / 2;
      var hPadding = 2;
      var vPadding = 2;

      //右边实时价格背景
      var rrect = BorderRadius.circular(2).toRRect(Rect.fromLTRB(
        left - hPadding,
        top - vPadding,
        left + tp.width + hPadding,
        top + tp.height + vPadding,
      ));
      canvas.drawRRect(
          rrect,
          realTimePaint
            ..color = chartColors.rightRealTimeBgColorFn?.call(isUp) ??
                realTimePaint.color);
      tp.paint(canvas, Offset(left, top));

      onRealtimeRectNotifier?.value = null;
    } else {
      stopAnimation(); //停止一闪闪
      startX = 0;
      if (point.close > mMainMaxValue) {
        y = getMainY(mMainMaxValue);
      } else if (point.close < mMainMinValue) {
        y = getMainY(mMainMinValue);
      }
      while (startX < mWidth) {
        canvas.drawLine(Offset(startX, y), Offset(startX + dashWidth, y),
            realTimePaint..color = realTimeLongLineColor!);
        startX += space;
      }

      const padding = 4.0;
      const triangleHeight = 6.0; //三角高度
      const triangleWidth = 4.0; //三角宽度

      double left = mWidth -
          mWidth / chartStyle.gridColumns! -
          tp.width -
          padding * 2 -
          triangleWidth -
          padding;
      // double left = mWidth - 76 - tp.width  - padding * 2;
      double top = y - tp.height / 2 - padding;
      //加上三角形的宽以及padding
      double right = left + tp.width + padding * 2 + triangleWidth + padding;
      double bottom = top + tp.height + padding * 2;
      double radius = (bottom - top) / 2;
      var strokeWidth = realTimePaint.strokeWidth;
      //K线图内实时价格的椭圆背景位置
      RRect rectBg1 =
          RRect.fromLTRBR(left, top, right, bottom, Radius.circular(radius));
      onRealtimeRectNotifier?.value = Rect.fromLTRB(left, top, right, bottom);
      RRect rectBg2 = RRect.fromLTRBR(
        left - strokeWidth,
        top - strokeWidth,
        right + strokeWidth,
        bottom + strokeWidth,
        Radius.circular(radius + 2),
      );
      canvas.drawRRect(
        rectBg2,
        realTimePaint
          ..color = chartColors.realTimeTextBorderColorFn?.call(isUp) ??
              realTimePaint.color
          ..style = PaintingStyle.stroke,
      );
      canvas.drawRRect(
        rectBg1,
        realTimePaint
          ..color =
              chartColors.realTimeBgColorFn?.call(isUp) ?? realTimePaint.color
          ..style = PaintingStyle.fill,
      );
      tp = getTextPainter(format(point.close), color: realTimeTextColor);
      Offset textOffset = Offset(left + padding, y - tp.height / 2);
      tp.paint(canvas, textOffset);
      //画三角
      double dx = tp.width + textOffset.dx + padding;
      double dy = top + (bottom - top - triangleHeight) / 2;
      Path path = Path();
      path.moveTo(dx, dy);
      path.lineTo(dx + triangleWidth, dy + triangleHeight / 2);
      path.lineTo(dx, dy + triangleHeight);
      path.close();
      canvas.drawPath(
        path,
        realTimePaint
          ..color = realTimeTextColor!
          ..shader = null
          ..strokeJoin = StrokeJoin.round,
      );
    }
  }

  TextPainter getTextPainter(
    text, {
    color = Colors.white,
    FontWeight? fontWeight,
    double? fontSize,
    double? height,
    Color? backgroundColor,
  }) {
    TextSpan span = TextSpan(
        text: "$text",
        style: getTextStyle(color).copyWith(
          fontWeight: fontWeight,
          fontSize: fontSize,
          height: height,
          backgroundColor: backgroundColor,
        ));
    TextPainter tp = TextPainter(text: span, textDirection: TextDirection.ltr);
    tp.layout();
    return tp;
  }

  String getDate(int date) => DateFormatUtil.dateFormat(
      DateTime.fromMillisecondsSinceEpoch(date * 1000), mFormats);

  double getMainY(double y) => mMainRenderer?.getY(y) ?? 0.0;

  startAnimation() {
    if (controller?.isAnimating != true) controller?.repeat(reverse: true);
  }

  stopAnimation() {
    if (controller?.isAnimating == true) controller?.stop();
  }
}
